<?php
/*********************************************
 *  CPG Dragonfly™ CMS
 *********************************************
	Copyright © since 2010 by CPG-Nuke Dev Team
	http://dragonflycms.org

	Dragonfly is released under the terms and conditions
	of the GNU GPL version 2 or any later version
*/

namespace Poodle;

abstract class Hash
{
	const
		BCRYPT_DEFAULT_WORK_FACTOR = 10;

	public static function available($algo)
	{
		return ('bcrypt' === $algo || in_array($algo, hash_algos()) || function_exists($algo));
	}

	public static function file($algo, $filename, $raw=false)
	{
		if (in_array($algo, hash_algos())) {
			return hash_file($algo, $filename, $raw);
		}
		$algo = $algo.'_file';
		if (function_exists($algo)) {
			return $algo($filename, $raw);
		}
		return false;
	}

	public static function string($algo, $string, $raw=false)
	{
		switch ($algo)
		{
		case 'none':
		case '':
			return $string;

		case 'bcrypt':
			return self::bcrypt($string);

		default: # sha1, md5, etc.
			if (in_array($algo, hash_algos())) {
				return hash($algo, $string, $raw);
			}
			if (function_exists($algo)) {
				return $algo($string, $raw);
			}
		}
		return false;
	}

	public static function bcrypt($string, $work_factor = 0)
	{
		if ($work_factor < 4 || $work_factor > 31) $work_factor = self::BCRYPT_DEFAULT_WORK_FACTOR;
		// Handle PHP < 5.3.7 security weakness
		$salt = (version_compare(phpversion(), '5.3.7', '>=') ? '$2y$' : '$2a$');
		$salt .= str_pad($work_factor, 2, '0', STR_PAD_LEFT) . '$' .
			// the base64 function uses +'s and ending ='s; translate the first, and cut out the latter
			substr(str_replace('+','.',base64_encode(\Poodle\Random::bytes(16))), 0, 22)
		;
		$hash = crypt($string, $salt);
		return (strlen($hash) > 13) ? $hash : false;
	}

	public static function verify($algo, $string, $stored_hash, $raw=false)
	{
		$hash = false;
		switch ($algo)
		{
		case 'none':
		case '':
			$hash = $string;
			break;

		case 'bcrypt':
			$hash = crypt($string, $stored_hash);
			// Handle PHP < 5.3.7 security weakness
			if ($hash !== $stored_hash
			 && version_compare(phpversion(), '5.3.7', '>=')
			 && 0 === strpos($stored_hash, '$2a$')
			) {
				$stored_hash = '$2x$' .substr($stored_hash, 4);
				$hash = crypt($string, $stored_hash);
			}
			break;

		default: # sha1, md5, etc.
			if (in_array($algo, hash_algos())) {
				$hash = hash($algo, $string, $raw);
			} else
			if (function_exists($algo)) {
				$hash = $algo($string, $raw);
			}
		}

		return $hash === $stored_hash;
	}

	public static function hmac($algo, $data, $key, $raw=false)
	{
		if (in_array($algo, hash_algos())) {
			return hash_hmac($algo, $data, $key, $raw);
		}

		if ('sha1'==$algo && function_exists($algo))
		{
			if (mb_strlen($key,'8bit') > 64) { $key = sha1($key, true); }
			$key  = str_pad($key, 64, "\x00");
			$ipad = str_repeat(chr(0x36), 64);
			$opad = str_repeat(chr(0x5c), 64);
			$hash = sha1(($key ^ $ipad) . $data, true);
			return sha1(($key ^ $opad) . $hash, true);
		}

		return false;
	}

	public static function hmac_file($algo, $filename, $key, $raw=false)
	{
		if (in_array($algo, hash_algos())) {
			return hash_hmac_file($algo, $filename, $key, $raw);
		}
		return false;
	}

	public static function sha1($string, $raw=false) { return self::string('sha1', $string, $raw); }
	public static function md5($string,  $raw=false) { return self::string('md5',  $string, $raw); }

}
